﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;

namespace EventFlowLibrary.GameStructure
{
    public class SoundEffectInstanceRecycler
    {
        int currentIndex;
        SoundEffectInstance[] instances;

        public SoundEffectInstanceRecycler(SoundEffect _sound, int _numInstances)
        {
            currentIndex = 0;
            instances = new SoundEffectInstance[_numInstances];

            for (int i = 0; i < _numInstances; i++)
            {
                instances[i] = _sound.CreateInstance();
            }
        }

        ~SoundEffectInstanceRecycler()
        {
            for (int i = 0; i < instances.Length; i++)
            {
                
                if (!instances[i].IsDisposed)
                {
                    instances[i].Stop(true);
                    instances[i].Dispose();
                    GC.SuppressFinalize(instances[i]);
                }
            }
        }

        public void StopAll()
        {
            for (int i = 0; i < instances.Length; i++)
            {
                instances[i].Stop(true);
            }
        }

        /// <summary>
        /// Play a SoundEffectInstance at the current index in the array.
        /// </summary>
        /// <param name="_restart">
        /// Whether to stop and restart index if currently playing.
        /// </param>
        public void PlaySound(float _volume, float _pitch, float _pan, bool _restart)
        {
            // Restart if indicated.
            if (_restart && 
                (instances[currentIndex].State == SoundState.Playing ||
                instances[currentIndex].State == SoundState.Paused))
            {
                instances[currentIndex].Stop(true);
            }

            // Set volume, pan, pitch
            instances[currentIndex].Volume = _volume;
            instances[currentIndex].Pitch = _pitch;
            instances[currentIndex].Pan = _pan;

            // Play and increment
            instances[currentIndex].Play();
            currentIndex++;

            // Reset the index if necessary
            if (currentIndex >= instances.Length)
            {
                currentIndex = 0;
            }
        }
    }

    public static class AudioManager
    {
        private static Random rand = new Random();

        public static List<Song> backgroundMusic = new List<Song>();

        public static Dictionary<string, SoundEffectInstanceRecycler> soundEffects = new Dictionary<string, SoundEffectInstanceRecycler>();
        private static Dictionary<string, SoundEffect> _soundEffects = new Dictionary<string, SoundEffect>();

        public static void LoadContent(ContentManager _content)
        {
            // Load background music
            AudioManager.backgroundMusic.Add(_content.Load<Song>("Music/Revealing_the_Truth"));
            AudioManager.backgroundMusic.Add(_content.Load<Song>("Music/Nothing_Ever_Lasts"));
            AudioManager.backgroundMusic.Add(_content.Load<Song>("Music/Revealing_the_Truth"));

            // TODO export from constructor
            if (!AudioManager.soundEffects.ContainsKey("typeEndSound"))
            {
                AudioManager._soundEffects.Add("typeEndSound", _content.Load<SoundEffect>("Audio/typesetting_break1"));
                AudioManager.soundEffects.Add("typeEndSound", new SoundEffectInstanceRecycler(AudioManager._soundEffects["typeEndSound"], 2));
                //AudioManager.soundEffects.Add("typeEndSound", _soundEffects["typeEndSound"].CreateInstance());
            }
            if (!AudioManager.soundEffects.ContainsKey("typeSound"))
            {
                AudioManager._soundEffects.Add("typeSound", _content.Load<SoundEffect>("Audio/typesetting1"));
                AudioManager.soundEffects.Add("typeSound", new SoundEffectInstanceRecycler(AudioManager._soundEffects["typeSound"], 20));
                //AudioManager.soundEffects.Add("typeSound", _soundEffects["typeSound"].CreateInstance());
            }
            if (!AudioManager.soundEffects.ContainsKey("DoorLocked"))
            {
                AudioManager._soundEffects.Add("DoorLocked", _content.Load<SoundEffect>("Audio/door_locked"));
                AudioManager.soundEffects.Add("DoorLocked", new SoundEffectInstanceRecycler(AudioManager._soundEffects["DoorLocked"], 2));
                //AudioManager.soundEffects.Add("DoorLocked", _soundEffects["DoorLocked"].CreateInstance());
            }
            if (!AudioManager.soundEffects.ContainsKey("DoorLocked2"))
            {
                AudioManager._soundEffects.Add("DoorLocked2", _content.Load<SoundEffect>("Audio/door_locked2"));
                AudioManager.soundEffects.Add("DoorLocked2", new SoundEffectInstanceRecycler(AudioManager._soundEffects["DoorLocked2"], 2));
                //AudioManager.soundEffects.Add("DoorLocked", _soundEffects["DoorLocked"].CreateInstance());
            }
            if (!AudioManager.soundEffects.ContainsKey("footstep1"))
            {
                AudioManager._soundEffects.Add("footstep1", _content.Load<SoundEffect>("Audio/footstep1"));
                AudioManager.soundEffects.Add("footstep1", new SoundEffectInstanceRecycler(AudioManager._soundEffects["footstep1"], 2));
                //AudioManager.soundEffects.Add("footstep1", _soundEffects["footstep1"].CreateInstance());
            }
            if (!AudioManager.soundEffects.ContainsKey("pageflip3"))
            {
                AudioManager._soundEffects.Add("pageflip3", _content.Load<SoundEffect>("Audio/pageflip3"));
                AudioManager.soundEffects.Add("pageflip3", new SoundEffectInstanceRecycler(AudioManager._soundEffects["pageflip3"], 10));
                //AudioManager.soundEffects.Add("footstep1", _soundEffects["footstep1"].CreateInstance());
            }
            if (!AudioManager.soundEffects.ContainsKey("pageflip12"))
            {
                AudioManager._soundEffects.Add("pageflip12", _content.Load<SoundEffect>("Audio/pageflip12"));
                AudioManager.soundEffects.Add("pageflip12", new SoundEffectInstanceRecycler(AudioManager._soundEffects["pageflip12"], 1));
                //AudioManager.soundEffects.Add("footstep1", _soundEffects["footstep1"].CreateInstance());
            }
        }

        public static void UnloadContent()
        {
            MediaPlayer.MediaStateChanged -= MediaPlayer_MediaStateChanged;
            MediaPlayer.Stop();

            for (int i = 0; i < backgroundMusic.Count; i++)
            {
                if (!backgroundMusic[i].IsDisposed)
                {
                    backgroundMusic[i].Dispose();
                    GC.SuppressFinalize(backgroundMusic[i]);
                }
            }

            List<string> l = soundEffects.Keys.ToList();
            for (int i = 0; i < l.Count; i++)
            {
                soundEffects[l[i]].StopAll();
                soundEffects[l[i]] = null;
            }

            //foreach (KeyValuePair<string, SoundEffectInstanceRecycler> k in soundEffects)
            //{
            //    k.Value.StopAll();
            //    soundEffects[k.Key] = null;
            //    //if (!k.Value.IsDisposed)
            //    //{
            //    //    k.Value.Dispose();
            //    //    GC.SuppressFinalize(k);
            //    //}
            //}

            foreach (KeyValuePair<string, SoundEffect> k in _soundEffects)
            {
                if (!k.Value.IsDisposed)
                {
                    k.Value.Dispose();
                    GC.SuppressFinalize(k);
                }
            }
        }

        public static void MediaPlayer_MediaStateChanged(object sender, EventArgs e)
        {
            if (Microsoft.Xna.Framework.Media.MediaPlayer.State == MediaState.Stopped)
            {
                Microsoft.Xna.Framework.Media.MediaPlayer.Play(AudioManager.backgroundMusic[rand.Next(0, AudioManager.backgroundMusic.Count)]);
            }
        }
    }
}
